﻿using System;
using System.Net;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace Gmantis.Controls.DataGrid
{
    public class DataGridNumericColumn : DataGridBoundColumn
    {
        // Fields
        public static readonly DependencyProperty HandleUpDownKeysProperty = DependencyProperty.Register("HandleUpDownKeys", typeof(bool), typeof(DataGridNumericColumn), new PropertyMetadata(true));
        public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register("Maximum", typeof(double), typeof(DataGridNumericColumn), new PropertyMetadata(1.7976931348623157E+308));
        public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register("Minimum", typeof(double), typeof(DataGridNumericColumn), new PropertyMetadata(-1.7976931348623157E+308));
        public static readonly DependencyProperty RangeValidationModeProperty = DependencyProperty.Register("RangeValidationMode", typeof(RangeValidationMode), typeof(DataGridNumericColumn), new PropertyMetadata(RangeValidationMode.OnLostFocus));
        public static readonly DependencyProperty ShowButtonsProperty = DependencyProperty.Register("ShowButtons", typeof(bool), typeof(DataGridNumericColumn), new PropertyMetadata(true));

        // Methods
        public DataGridNumericColumn()
        {
            this.Initialize();
        }

        public DataGridNumericColumn(PropertyInfo property)
            : base(property)
        {
            this.Initialize();
            Type nonNullableType = property.PropertyType.GetNonNullableType();
            if (nonNullableType.IsNumeric())
            {
                if (nonNullableType.IsNumericIntegral())
                {
                    if (string.IsNullOrEmpty(this.Format))
                    {
                        this.Format = "N0";
                    }
                    if (nonNullableType.IsNumericIntegralUnsigned())
                    {
                        this.Minimum = 0.0;
                    }
                }
                else if (string.IsNullOrEmpty(this.Format))
                {
                    this.Format = "N";
                }
            }
        }

        public override bool BeginEdit(FrameworkElement editingElement, RoutedEventArgs routedEventArgs)
        {
            if ((editingElement is NumericBox) && (routedEventArgs is TextCompositionEventArgs))
            {
                return (editingElement as NumericBox).BeginEdit(routedEventArgs as TextCompositionEventArgs);
            }
            return true;
        }

        public override void CancelCellEdit(FrameworkElement editingElement, object uneditedValue)
        {
            (editingElement as NumericBox).Value = (double)uneditedValue;
        }

        public override void EndEdit(FrameworkElement editingElement)
        {
            NumericBox box = editingElement as NumericBox;
            if (box != null)
            {
                box.SetValue(box.Value, true);
                BindingExpression bindingExpression = box.GetBindingExpression(NumericBox.ValueProperty);
                if (bindingExpression != null)
                {
                    bindingExpression.UpdateSource();
                }
            }
        }

        public override FrameworkElement GetCellEditingContent(DataGridRow row)
        {
            NumericBox box = new NumericBox();
            try
            {
                box.Format = this.GetActualFormat();
            }
            catch
            {
                box.Format = "";
            }
            box.SetBinding(NumericBox.AllowNullProperty, new Binding().From<DataGridNumericColumn>(this, x => x.AllowNull));
            box.SetBinding(NumericBox.ShowButtonsProperty, new Binding().From<DataGridNumericColumn>(this, x => x.ShowButtons));
            box.SetBinding(NumericBox.MinimumProperty, new Binding().From<DataGridNumericColumn>(this, x => x.Minimum));
            box.SetBinding(NumericBox.MaximumProperty, new Binding().From<DataGridNumericColumn>(this, x => x.Maximum));
            box.SetBinding(NumericBox.RangeValidationModeProperty, new Binding().From<DataGridNumericColumn>(this, x => x.RangeValidationMode));
            box.SetBinding(NumericBox.HandleUpDownKeysProperty, new Binding().From<DataGridNumericColumn>(this, x => x.HandleUpDownKeys));
            box.Culture = this.GetActualCulture();
            box.Padding = new Thickness(0.0);
            box.CornerRadius = new CornerRadius(0.0);
            if (base.Binding != null)
            {
                Binding binding = base.CopyBinding(base.Binding, null);
                box.SetBinding(NumericBox.ValueProperty, binding);
            }
            box.TextAlignment = (base.HorizontalAlignment == HorizontalAlignment.Left) ? TextAlignment.Left : ((base.HorizontalAlignment == HorizontalAlignment.Right) ? TextAlignment.Right : TextAlignment.Center);
            box.VerticalContentAlignment = base.VerticalAlignment;
            if (base.CellEditingContentStyle != null)
            {
                box.Style = base.CellEditingContentStyle;
            }
            return box;
        }

        public override IDataGridFilter GetFilter()
        {
            DataGridFilter filter = new DataGridFilter();
            DataGridNumericFilter target = new DataGridNumericFilter
            {
                Format = this.GetActualFormat()
            };
            target.SetBinding<DataGrid>(Control.BackgroundProperty, base.DataGrid, dg => dg.HeaderBackground);
            target.SetBinding<DataGrid>(Control.ForegroundProperty, base.DataGrid, dg => dg.HeaderForeground);
            target.SetBinding<DataGrid>(Control.BorderBrushProperty, base.DataGrid, dg => dg.BorderBrush);
            target.SetBinding<DataGrid>(DataGridNumericFilter.MouseOverBrushProperty, base.DataGrid, dg => dg.MouseOverBrush);
            target.SetBinding<DataGrid>(DataGridNumericFilter.PressedBrushProperty, base.DataGrid, dg => dg.PressedBrush);
            target.SetBinding<DataGrid>(DataGridNumericFilter.SelectedBackgroundProperty, base.DataGrid, dg => dg.SelectedBackground);
            target.SetBinding<DataGrid>(DataGridNumericFilter.FocusBrushProperty, base.DataGrid, dg => dg.SelectedBackground);
            target.SetBinding<DataGrid>(DataGridNumericFilter.InputBackgroundProperty, base.DataGrid, dg => dg.RowBackground);
            target.SetBinding<DataGrid>(DataGridNumericFilter.InputForegroundProperty, base.DataGrid, dg => dg.RowForeground);
            filter.InnerControl = target;
            return filter;
        }

        private void Initialize()
        {
            this.Format = "";
            base.HorizontalAlignment = HorizontalAlignment.Right;
            base.TextTrimming = TextTrimming.None;
        }

        public override object PrepareCellForEdit(FrameworkElement editingElement)
        {
            return (editingElement as NumericBox).Value;
        }

        // Properties
        public override IValueConverter DefaultConverter
        {
            get
            {
                return new DataGridNumericConverter();
            }
        }

        public bool HandleUpDownKeys
        {
            get
            {
                return (bool)base.GetValue(HandleUpDownKeysProperty);
            }
            set
            {
                base.SetValue(HandleUpDownKeysProperty, value);
            }
        }

        public override bool IsEditable
        {
            get
            {
                return (base.Binding != null);
            }
        }

        public double Maximum
        {
            get
            {
                return (double)base.GetValue(MaximumProperty);
            }
            set
            {
                base.SetValue(MaximumProperty, value);
            }
        }

        public double Minimum
        {
            get
            {
                return (double)base.GetValue(MinimumProperty);
            }
            set
            {
                base.SetValue(MinimumProperty, value);
            }
        }

        public RangeValidationMode RangeValidationMode
        {
            get
            {
                return (RangeValidationMode)base.GetValue(RangeValidationModeProperty);
            }
            set
            {
                base.SetValue(RangeValidationModeProperty, value);
            }
        }

        public bool ShowButtons
        {
            get
            {
                return (bool)base.GetValue(ShowButtonsProperty);
            }
            set
            {
                base.SetValue(ShowButtonsProperty, value);
            }
        }
    }
}
